Skip to main content

Querying DataFrames

A filter is an index-aligned boolean Series.

mask = orders["amount"].ge(100) & orders["status"].isin(["paid", "shipped"])
large_orders = orders.loc[mask, ["customer_id", "amount", "status"]]

Predicate rules

  • Parenthesize each comparison when combining with &, |, or ~.
  • Use isin for membership, between for closed ranges, and isna/notna for missingness.
  • Use .str, .dt, and .cat accessors for typed string, datetime, and categorical operations.
  • Check the mask's index when it was built from another object; alignment can change the selected rows.
recent = orders.loc[orders["created_at"].between("2026-01-01", "2026-03-31")]
missing_customer = orders.loc[orders["customer_id"].isna()]

query

DataFrame.query can make long analytical expressions readable:

threshold = 100
result = orders.query("amount >= @threshold and status == 'paid'")

Use ordinary boolean expressions when column names are awkward, predicates are constructed dynamically, or normal Python debugging is more valuable than the compact syntax. Never treat a query string assembled from untrusted input as a security boundary.

Filter versus select

Filtering chooses rows by a predicate. .loc can simultaneously choose rows and columns; .iloc chooses by positions. Keeping those ideas separate prevents many shape and label mistakes.

Source